﻿using System;
using System.Linq;
using ProLinq.Interception;
using ProLinq.Projection;
using ProLinq.Projection.Configuration;
using ProLinq.Toolkit;

namespace ProLinq
{
	public static class QueryableDataExtensions
	{
		#region Projection

		/// <summary>
		///     Projects an IQueryable source to another type
		/// </summary>
		/// <typeparam name="TSource"></typeparam>
		/// <param name="source"></param>
		/// <returns></returns>
		public static IProjectionSource<TSource> Project<TSource>(this IQueryable<TSource> source)
		{
			return new ProjectionSource<TSource>(source);
		}


		/// <summary>
		/// Projects an IQueryable source using specified configuration
		/// </summary>
		/// <param name="source"></param>
		/// <param name="configuration"></param>
		/// <returns></returns>
		public static IQueryable Project(this IQueryable source, ProjectionConfiguration configuration)
		{
			var provider = new ProjectQueryProvider(source, configuration);

			var elementType = configuration.Projections.Where(p => p.SourceType == source.ElementType)
			                             .Select(p => p.DestinationType).FirstOrDefault() ?? typeof (Object);

			return Query.Create(elementType, provider);
		}

		public static IQueryable Project(this IQueryable source, Action<ProjectionConfiguration> configure)
		{
			var configuration = ProjectionConfigurationFactory.Instance.CreateConfiguration();
			configure(configuration);

			return Project(source, configuration);
		}


		#endregion

		#region Interception

		/// <summary>
		///     Places an interceptor for query execution
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="source"></param>
		/// <param name="action"></param>
		/// <returns></returns>
		public static IQueryable<T> Intercept<T>(this IQueryable<T> source, InterceptionAction action)
		{
			InterceptionQuery<T> query = source as InterceptionQuery<T> ?? new InterceptionQuery<T>(source);

			query.Actions.Add(action);

			return query;
		}

		/// <summary>
		///     Places an interceptor for query execution
		/// </summary>
		/// <param name="source"></param>
		/// <param name="action"></param>
		/// <returns></returns>
		public static IQueryable Intercept(this IQueryable source, InterceptionAction action)
		{
			var query = source as IInterceptionQuery;
			if (query == null)
			{
				var queryType = typeof(InterceptionQuery<>).MakeGenericType(source.ElementType);
				query = (IInterceptionQuery)Activator.CreateInstance(queryType, source);
			}

			query.Actions.Add(action);

			return (IQueryable)query;
		}

		#endregion
	}
}